#include "oopless-parser.hpp"
#include "brushdef.hpp"
#include <sstream>
#include <iostream>
#include <iterator>
using namespace std;

bool is_ebrush(const std::vector<std::string> &input) {
using namespace std;
#define KEYWORD_ENTBRUSH1 "type Teleporter"
#define KEYWORD_ENTBRUSH2 "type JumpPad"
#define KEYWORD_ENTBRUSH3 "type RaceStart"
#define KEYWORD_ENTBRUSH4 "type RaceFinish"
    for (const string &str : input) {
        if (str.find(KEYWORD_ENTBRUSH1) != string::npos ||
            str.find(KEYWORD_ENTBRUSH2) != string::npos ||
            str.find(KEYWORD_ENTBRUSH3) != string::npos ||
            str.find(KEYWORD_ENTBRUSH4) != string::npos) {
                return true;
        }
    }
    return false;
}


vector<string> extract_brush(const vector<string> &entity) {
    bool brushent = false;
    unsigned int i;
    for (i = 0; i < entity.size(); ++i) {
        if (entity[i].find(KEYWORD_BRUSH) != string::npos) {
            brushent = true;
            break;
        }
    }
    vector<string> output(entity.size() - i);
    if (brushent) {
        // entity.begin() + i = vector that begins with KEYWORD_BRUSH
        // parsing functions will expect only the contents underneath the keyword.
        move((entity.begin() + i), entity.end(), output.begin());
    }
    return output;
}

// place holding functions to test proper control flow.
bool write(const struct TBrush &geometry, 
           ofstream &fout,
           void (*b) (stringstream &, const vector<TPlanePoints> &)) {
    bool ok = true;
    if (geometry.m_Vertices.size() < 1) {
        cerr << "error: geometry has no vertices" << endl;
        ok = false;
    }
    if (geometry.m_Faces.size() < 1) {
        cerr << "error: geometry has no faces"<< endl;
        ok = false;
    }
    if (ok) {
        fout << GetBrushString(geometry, b);
    }
    return ok;
}

// currently for the specific case to specify trigger to brush entities
struct TBrush override_textures(struct TBrush x, const char *texture) {
    struct TBrush output = x;
    for (struct TFace &z : output.m_Faces) {
        // prevent appending color infomation to texture file path
        // may need re-ordering on when to append that.
        z.hex = string();
        z.m_Material = texture;
    }
    return output;
}

void write(const vector<string> &entity, 
           ofstream &fo,
           void (*b) (stringstream &, const vector<TPlanePoints> &),
           EntityConverter &e) {
/*  Likely inefficient.
    Current process for the entities: 
    1. It acquires the lines. 
    2. Searches through and splits the vector.
    3. Recombines the brush vector.
    (4. Sends the vector to parse the brush through again.)*/
    vector<string> brush = extract_brush(entity);
    vector<string> converted = e.convert(entity);
    if (converted.empty()) {
        return;
    }
    fo << "{" << endl;
    for (const string &s : converted) {
        fo << s;
    }
    if (brush.size() > 0) {
        stringstream ss;
        string line;
        copy(brush.begin(), brush.end(), ostream_iterator<string>(ss, "\n"));
        getline(ss, line); // this both discards the brush keyboard and prevents a crash
        write(override_textures(parse_brush<stringstream>(ss), "common/trigger"), fo, b);
    }
    fo << "}" << endl;
}

bool convertmap(stringstream &mapdata, 
                const char * const outfile,
                void (*brushdef) (stringstream &, const vector<TPlanePoints> &),
                vector<vector<string> > &entities) {
    ofstream fout;
    fout.open(outfile);
    if (!fout.good()) {
        cerr << "error: failed to open output file" << endl;
        return false;
    }
    fout << "{\n"
         << "\"classname\" \"worldspawn\"" << endl;
    string line;
    while (getline(mapdata, line)) {
        if (line.find(KEYWORD_PREFAB) != string::npos ||
            line.find(KEYWORD_GLOBAL) != string::npos) {
                parse_prefab<stringstream>(mapdata, fout, brushdef, entities);
        } else if (line.find(KEYWORD_ENTITY) != string::npos) {
                entities.push_back(get_entity<stringstream>(mapdata));
        } else if (line.find(KEYWORD_BRUSH) != string::npos) {
                write(parse_brush<stringstream>(mapdata), fout, brushdef);
        }
    }
    fout << "}" << endl;
    fout.close();
    return true;
}
